home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / c-client / os_mac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-14  |  18.7 KB  |  654 lines

  1. /*
  2.  * Program:    Operating-system dependent routines -- Macintosh version
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        6158 Lariat Loop NE
  6.  *        Bainbridge Island, WA  98110-2098
  7.  *        Internet: MRC@Panda.COM
  8.  *
  9.  * Date:    26 January 1992
  10.  * Last Edited:    14 May 1992
  11.  *
  12.  * Copyright 1992 by Mark Crispin
  13.  *
  14.  *  Permission to use, copy, modify, and distribute this software and its
  15.  * documentation for any purpose and without fee is hereby granted, provided
  16.  * that the above copyright notice appears in all copies and that both the
  17.  * above copyright notices and this permission notice appear in supporting
  18.  * documentation, and that the name of Mark Crispin not be used in advertising
  19.  * or publicity pertaining to distribution of the software without specific,
  20.  * written prior permission.  This software is made available "as is", and
  21.  * MARK CRISPIN DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO
  22.  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF
  23.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL
  24.  * MARK CRISPIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
  25.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  26.  * WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT
  27.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  28.  * THIS SOFTWARE.
  29.  *
  30.  */
  31.  
  32.  
  33. /*  This is a totally new operating-system dependent module for the Macintosh,
  34.  * written using THINK C on my Mac PowerBook-100 in my free time.
  35.  * Unlike earlier efforts, this version requires no external TCP library.  It
  36.  * also takes advantage of the Map panel in System 7 for the timezone.
  37.  */
  38.  
  39. #define BUFLEN (size_t) 8192    /* TCP input buffer */
  40.  
  41.  
  42. #include <types.h>
  43. #include <limits.h>
  44. #include <time.h>
  45. #include <stdio.h>
  46. #include <MacTCPCommonTypes.h>
  47. #include <AddressXlation.h>
  48. #include <TCPPB.h>
  49. #include <Script.h>
  50.  
  51. /* TCP I/O stream (must be before osdep.h is included) */
  52.  
  53. #define TCPSTREAM struct tcp_stream
  54. TCPSTREAM {
  55.   char *host;            /* host name */
  56.   char *localhost;        /* local host name */
  57.   struct TCPiopb pb;        /* MacTCP parameter block */
  58.   long ictr;            /* input counter */
  59.   char *iptr;            /* input pointer */
  60.   char ibuf[BUFLEN];        /* input buffer */
  61. };
  62.  
  63. #include "osdep.h"
  64. #include "mail.h"
  65. #include "misc.h"
  66.  
  67. short TCPdriver = 0;        /* MacTCP's reference number */
  68. short resolveropen = 0;        /* TCP's resolver open */
  69.  
  70. /* Write current time in RFC 822 format
  71.  * Accepts: destination string
  72.  *
  73.  * This depends upon the ReadLocation() call in System 7 and the
  74.  * user properly setting his location/timezone in the Map control
  75.  * panel.
  76.  * Nothing is done about the gmtFlags.dlsDelta byte yet, since I
  77.  * don't know how it's supposed to work.
  78.  */
  79.  
  80. void rfc822_date (char *string)
  81. {
  82.   long tz,tzm;
  83.   time_t ti = time (0);
  84.   struct tm *t = localtime (&ti);
  85.   MachineLocation loc;
  86.   ReadLocation (&loc);        /* get location/timezone poop */
  87.   tz = loc.gmtFlags.gmtDelta |    /* get sign-extended time zone */
  88.     ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0);
  89.   tz /= 60;            /* get timezone in minutes */
  90.   tzm = tz % 60;        /* get minutes from the hour */
  91.                 /* output time */
  92.   strftime (string,MAILTMPLEN,"%a, %d %b %Y %H:%M:%S ",t);
  93.                 /* now output time zone */
  94.   sprintf (string += strlen (string),"%+03ld%02ld",
  95.        tz/60,tzm >= 0 ? tzm : -tzm);
  96. }
  97.  
  98. /* Get a block of free storage
  99.  * Accepts: size of desired block
  100.  * Returns: free storage block
  101.  */
  102.  
  103. void *fs_get (size_t size)
  104. {
  105.   void *block = malloc (size);
  106.   if (!block) fatal ("Out of free storage");
  107.   return (block);
  108. }
  109.  
  110.  
  111. /* Resize a block of free storage
  112.  * Accepts: ** pointer to current block
  113.  *        new size
  114.  */
  115.  
  116. void fs_resize (void **block,size_t size)
  117. {
  118.   if (!(*block = realloc (*block,size))) fatal ("Can't resize free storage");
  119. }
  120.  
  121.  
  122. /* Return a block of free storage
  123.  * Accepts: ** pointer to free storage block
  124.  */
  125.  
  126. void fs_give (void **block)
  127. {
  128.   free (*block);
  129.   *block = NIL;
  130. }
  131.  
  132.  
  133. /* Report a fatal error
  134.  * Accepts: string to output
  135.  */
  136.  
  137. void fatal (char *string)
  138. {
  139.   mm_fatal (string);        /* pass up the string */
  140.                 /* nuke the resolver */
  141.   if (resolveropen) CloseResolver ();
  142.   abort ();            /* die horribly */
  143. }
  144.  
  145. /* Copy string with CRLF newlines
  146.  * Accepts: destination string
  147.  *        pointer to size of destination string
  148.  *        source string
  149.  *        length of source string
  150.  */
  151.  
  152. char *strcrlfcpy (char **dst,unsigned long *dstl,char *src,unsigned long srcl)
  153. {
  154.   long i,j;
  155.   char *d = src;
  156.                 /* count number of LF's in source string(s) */
  157.   for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
  158.   if (i > *dstl) {        /* resize if not enough space */
  159.     fs_give ((void **) dst);    /* fs_resize does an unnecessary copy */
  160.     *dst = (char *) fs_get ((*dstl = i) + 1);
  161.   }
  162.   d = *dst;            /* destination string */
  163.   while (srcl--) {        /* copy strings */
  164.     *d++ = *src++;        /* copy character */
  165.                 /* append line feed to bare CR */
  166.     if ((*src == '\015') && (src[1] != '\012')) *d++ = '\012';
  167.   }
  168.   *d = '\0';            /* tie off destination */
  169.   return *dst;            /* return destination */
  170. }
  171.  
  172.  
  173. /* Length of string after strcrlflen applied
  174.  * Accepts: source string
  175.  *        length of source string
  176.  */
  177.  
  178. unsigned long strcrlflen (char *src,unsigned long srcl)
  179. {
  180.   long i = srcl;        /* look for LF's */
  181.   while (srcl--) if ((*src == '\015') && (src[1] != '\012')) i++;
  182.   return i;
  183. }
  184.  
  185. /* Server log in (dummy place holder)
  186.  * Accepts: user name string
  187.  *        password string
  188.  *        optional place to return home directory
  189.  * Returns: T if password validated, NIL otherwise
  190.  */
  191.  
  192. long server_login (char *user,char *pass,char **home)
  193. {
  194.   return NIL;
  195. }
  196.  
  197. /* TCP/IP open
  198.  * Accepts: host name
  199.  *        contact port number
  200.  * Returns: TCP stream if success else NIL
  201.  */
  202.  
  203. TCPSTREAM *tcp_open (char *host,long port)
  204. {
  205.   TCPSTREAM *stream;
  206.   struct hostInfo hst;
  207.   struct TCPCreatePB *createpb;
  208.   struct TCPOpenPB *openpb;
  209.   char *s;
  210.   unsigned long i,j,k,l;
  211.   char tmp[MAILTMPLEN];
  212.                 /* init MacTCP */
  213.   if (!TCPdriver && OpenDriver ("\p.IPP",&TCPdriver))
  214.     fatal ("Can't init MacTCP");
  215.                 /* domain literal? */
  216.   if (host[0] == '[' && host[strlen (host)-1] == ']') {
  217.     if (((i = strtol (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
  218.     ((j = strtol (s,&s,10)) <= 255) && *s++ == '.' &&
  219.     ((k = strtol (s,&s,10)) <= 255) && *s++ == '.' &&
  220.     ((l = strtol (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
  221.       hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l;
  222.       hst.addr[1] = 0;        /* only one address to try! */
  223.       sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l);
  224.     }
  225.     else {
  226.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  227.       mm_log (tmp,ERROR);
  228.       return NIL;
  229.     }
  230.   }
  231.  
  232.   else {            /* look up host name */
  233.     if (!resolveropen && OpenResolver (NIL))
  234.       fatal ("Can't init domain resolver");
  235.     resolveropen = T;        /* note resolver open now */
  236.     if (StrToAddr (host,&hst,tcp_dns_result,NIL)) {
  237.       while (hst.rtnCode == cacheFault && wait ());
  238.       if (hst.rtnCode) {    /* still have error status? */
  239.     switch (hst.rtnCode) {    /* analyze return */
  240.     case nameSyntaxErr:
  241.       s = "Syntax error in name";
  242.       break;
  243.     case noResultProc:
  244.       s = "No result procedure";
  245.       break;
  246.     case noNameServer:
  247.       s = "No name server found";
  248.       break;
  249.     case authNameErr:
  250.       s = "Host does not exist";
  251.       break;
  252.     case noAnsErr:
  253.       s = "No name servers responding";
  254.       break;
  255.     case dnrErr:
  256.       s = "Name server returned an error";
  257.       break;
  258.     case outOfMemory:
  259.       s = "Not enough memory to resolve name";
  260.       break;
  261.     case notOpenErr:
  262.       s = "Driver not open";
  263.       break;
  264.     default:
  265.       s = NIL;
  266.       break;
  267.     }
  268.     if (s) sprintf (tmp,"%s: %.80s",s,host);
  269.     else sprintf (tmp,"Unknown resolver error (%ld): %.80s",
  270.               hst.rtnCode,host);
  271.     mm_log (tmp,ERROR);
  272.     return NIL;
  273.       }
  274.     }
  275.   }
  276.  
  277.                 /* create local TCP/IP stream */
  278.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  279.   stream->ictr = 0;        /* initialize input */
  280.   stream->pb.ioCRefNum = TCPdriver;
  281.   createpb = &stream->pb.csParam.create;
  282.   openpb = &stream->pb.csParam.open;
  283.   stream->pb.csCode = TCPCreate;/* create a TCP stream */
  284.                 /* set up buffer for TCP */
  285.   createpb->rcvBuffLen = 4*BUFLEN;
  286.   createpb->rcvBuff = fs_get (createpb->rcvBuffLen);
  287.   createpb->notifyProc = NIL;    /* no special notify procedure */
  288.   createpb->userDataPtr = NIL;
  289.   if (PBControlSync (&stream->pb)) fatal ("Can't create TCP stream");
  290.                   /* open TCP connection */
  291.   stream->pb.csCode = TCPActiveOpen;
  292.   openpb->ulpTimeoutValue = 30;    /* time out after 30 seconds */
  293.   openpb->ulpTimeoutAction = T;
  294.   openpb->validityFlags = timeoutValue|timeoutAction;
  295.                 /* remote host (should try all) */
  296.   openpb->remoteHost = hst.addr[0];
  297.   openpb->remotePort = port;    /* caller specified remote port */
  298.   openpb->localPort = 0;    /* generate a local port */
  299.   openpb->tosFlags = 0;        /* no special TOS */
  300.   openpb->precedence = 0;    /* no special precedence */
  301.   openpb->dontFrag = 0;        /* allow fragmentation */
  302.   openpb->timeToLive = 255;    /* standards say 60, UNIX uses 255 */
  303.   openpb->security = 0;        /* no special security */
  304.   openpb->optionCnt = 0;    /* no IP options */
  305.   openpb->options[0] = 0;
  306.   openpb->userDataPtr = NIL;    /* no special data pointer */
  307.   PBControlAsync (&stream->pb);    /* now open the connection */
  308.   while (stream->pb.ioResult == inProgress && wait ());
  309.   if (stream->pb.ioResult) {    /* got back error status? */
  310.     sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port);
  311.     mm_log (tmp,ERROR);
  312.                 /* nuke the buffer */
  313.     stream->pb.csCode = TCPRelease;
  314.     createpb->userDataPtr = NIL;
  315.     if (PBControlSync (&stream->pb)) fatal ("TCPRelease lossage");
  316.                 /* free its buffer */
  317.     fs_give ((void **) &createpb->rcvBuff);
  318.     fs_give ((void **) &stream);/* and the local stream */
  319.     return NIL;
  320.   }
  321.  
  322.                 /* copy host names for later use */
  323.   stream->host = cpystr (hst.cname);
  324.                 /* tie off trailing dot */
  325.   stream->host[strlen (stream->host) - 1] = '\0';
  326.   i = openpb->localHost >> 24;    /* the open gave us our address */
  327.   j = (openpb->localHost >> 16) & 0xff;
  328.   k = (openpb->localHost >> 8) & 0xff;
  329.   l = openpb->localHost & 0xff;
  330.   sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l);
  331.   stream->localhost = cpystr (tmp);
  332.   return stream;
  333. }
  334.  
  335.  
  336. /* Called when have return from DNS
  337.  * Accepts: host info pointer
  338.  *        user data pointer
  339.  */
  340.  
  341. pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr)
  342. {
  343.   /* dummy routine */
  344. }
  345.  
  346. /* TCP/IP authenticated open
  347.  * Accepts: host name
  348.  *        service name
  349.  * Returns: TCP/IP stream if success else NIL
  350.  */
  351.  
  352. TCPSTREAM *tcp_aopen (char *host,char *service)
  353. {
  354.   return NIL;            /* no authenticated opens on Mac */
  355. }
  356.  
  357. /* TCP/IP receive line
  358.  * Accepts: TCP/IP stream
  359.  * Returns: text line string or NIL if failure
  360.  */
  361.  
  362. char *tcp_getline (TCPSTREAM *stream)
  363. {
  364.   long n,m;
  365.   char *st,*ret,*stp;
  366.   char tmp[2];
  367.   struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
  368.   struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
  369.   while (stream->ictr < 1) {    /* if nothing in the buffer */
  370.     stream->pb.csCode = TCPRcv;    /* receive TCP data */
  371.                 /* wait forever */
  372.     receivepb->commandTimeoutValue = 0;
  373.     receivepb->rcvBuff = stream->ibuf;
  374.     receivepb->rcvBuffLen = BUFLEN;
  375.     receivepb->secondTimeStamp = 0;
  376.     receivepb->userDataPtr = NIL;
  377.     PBControlAsync (&stream->pb);/* now read the data */
  378.     while (stream->pb.ioResult == inProgress && wait ());
  379.     if (stream->pb.ioResult) {    /* punt if got an error */
  380.                     /* nuke connection */
  381.       stream->pb.csCode = TCPAbort;
  382.       abortpb->userDataPtr = NIL;
  383.       PBControlSync (&stream->pb);
  384.       return NIL;
  385.     }
  386.     stream->iptr = stream->ibuf;/* point at TCP buffer */
  387.     stream->ictr = receivepb->rcvBuffLen;
  388.   }
  389.   st = stream->iptr;        /* save start of string */
  390.   n = 0;            /* init string count */
  391.   while (stream->ictr--) {    /* look for end of line */
  392.                 /* saw the trailing CR? */
  393.     if (stream->iptr++[0] == '\015') {
  394.       ret = (char *) fs_get (n+1);
  395.       memcpy (ret,st,n);    /* copy into a free storage string */
  396.       ret[n] = '\0';        /* tie off string with null */
  397.                 /* eat the line feed */
  398.       tcp_getbuffer (stream,1,tmp);
  399.       return ret;        /* return it to caller */
  400.     }
  401.     ++n;            /* else count and try next character */
  402.   }
  403.   stp = (char *) fs_get (n);    /* copy first part of string */
  404.   memcpy (stp,st,n);
  405.                 /* recurse to get remainder */
  406.   if (st = tcp_getline (stream)) {
  407.                 /* build total string */
  408.     ret = (char *) fs_get (n+1+(m = strlen (st)));
  409.     memcpy (ret,stp,n);        /* copy first part */
  410.     memcpy (ret+n,st,m);    /* and second part */
  411.     ret[n+m] = '\0';        /* tie off string with null */
  412.     fs_give ((void **) &st);    /* flush partial string */
  413.     fs_give ((void **) &stp);    /* flush initial fragment */
  414.   }
  415.   else ret = stp;        /* return the fragment */
  416.   return ret;
  417. }
  418.  
  419. /* TCP/IP receive buffer
  420.  * Accepts: TCP/IP stream
  421.  *        size in bytes
  422.  *        buffer to read into
  423.  * Returns: T if success, NIL otherwise
  424.  */
  425.  
  426. long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
  427. {
  428.   struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
  429.   struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
  430.   unsigned long n;
  431.   char *bufptr = buffer;
  432.   while (size > 0) {        /* until request satisfied */
  433.     while (stream->ictr < 1) {    /* if nothing in the buffer */
  434.       stream->pb.csCode =TCPRcv;/* receive TCP data */
  435.                 /* wait forever */
  436.       receivepb->commandTimeoutValue = 0;
  437.       receivepb->rcvBuff = stream->ibuf;
  438.       receivepb->rcvBuffLen = BUFLEN;
  439.       receivepb->rdsLength = 0;
  440.       receivepb->userDataPtr = NIL;
  441.       PBControlAsync (&stream->pb);
  442.       while (stream->pb.ioResult == inProgress && wait ());
  443.       if (stream->pb.ioResult) {/* punt if got an error */
  444.     stream->pb.csCode = TCPAbort;
  445.     abortpb->userDataPtr = NIL;
  446.     PBControlSync (&stream->pb);
  447.     return NIL;
  448.       }
  449.                 /* point at TCP buffer */
  450.       stream->iptr = stream->ibuf;
  451.       stream->ictr = receivepb->rcvBuffLen;
  452.     }
  453.     n = min (size,stream->ictr);/* number of bytes to transfer */
  454.                 /* do the copy */
  455.     memcpy (bufptr,stream->iptr,n);
  456.     bufptr += n;        /* update pointer */
  457.     stream->iptr +=n;
  458.     size -= n;            /* update # of bytes to do */
  459.     stream->ictr -=n;
  460.   }
  461.   bufptr[0] = '\0';        /* tie off string */
  462.   return T;
  463. }
  464.  
  465. /* TCP/IP send string as record
  466.  * Accepts: TCP/IP stream
  467.  * Returns: T if success else NIL
  468.  */
  469.  
  470. long tcp_soutr (TCPSTREAM *stream,char *string)
  471. {
  472.   struct TCPSendPB *sendpb = &stream->pb.csParam.send;
  473.   struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
  474.   struct {
  475.     unsigned short length;
  476.     Ptr buffer;
  477.     unsigned short trailer;
  478.   } wds;
  479.   stream->pb.csCode = TCPSend;    /* send TCP data */
  480.                 /* wait a maximum of 60 seconds */
  481.   sendpb->ulpTimeoutValue = 60;
  482.   sendpb->ulpTimeoutAction = 0;
  483.   sendpb->validityFlags = timeoutValue|timeoutAction;
  484.   sendpb->pushFlag = T;        /* send the data now */
  485.   sendpb->urgentFlag = NIL;    /* non-urgent data */
  486.   sendpb->wdsPtr = (Ptr) &wds;
  487.   sendpb->userDataPtr = NIL;
  488.   wds.length = strlen (string);    /* size of buffer */
  489.   wds.buffer = string;        /* buffer */
  490.   wds.trailer = 0;        /* tie off buffer */
  491.   PBControlAsync (&stream->pb);    /* now send the data */
  492.   while (stream->pb.ioResult == inProgress && wait ());
  493.   if (stream->pb.ioResult) {    /* punt if got an error */
  494.     stream->pb.csCode =TCPAbort;/* nuke connection */
  495.     abortpb->userDataPtr = NIL;
  496.     PBControlSync (&stream->pb);/* sayonara */
  497.     return NIL;
  498.   }
  499.   return T;            /* success */
  500. }
  501.  
  502. /* TCP/IP close
  503.  * Accepts: TCP/IP stream
  504.  */
  505.  
  506. void tcp_close (TCPSTREAM *stream)
  507. {
  508.   struct TCPClosePB *closepb = &stream->pb.csParam.close;
  509.   struct TCPCreatePB *createpb = &stream->pb.csParam.create;
  510.   stream->pb.csCode = TCPClose;    /* close TCP stream */
  511.   closepb->ulpTimeoutValue = 15;/* wait a maximum of 15 seconds */
  512.   closepb->ulpTimeoutAction = 0;
  513.   closepb->validityFlags = timeoutValue|timeoutAction;
  514.   closepb->userDataPtr = NIL;
  515.   PBControlAsync (&stream->pb);    /* now close the connection */
  516.   while (stream->pb.ioResult == inProgress && wait ());
  517.   stream->pb.csCode =TCPRelease;/* flush the buffers */
  518.   createpb->userDataPtr = NIL;
  519.   if (PBControlSync (&stream->pb)) fatal ("TCPRelease lossage");
  520.                 /* free its buffer */
  521.   fs_give ((void **) &createpb->rcvBuff);
  522.                 /* flush host names */
  523.   fs_give ((void **) &stream->host);
  524.   fs_give ((void **) &stream->localhost);
  525.   fs_give ((void **) &stream);    /* flush the stream */
  526. }
  527.  
  528. /* TCP/IP return host for this stream
  529.  * Accepts: TCP/IP stream
  530.  * Returns: host name for this stream
  531.  */
  532.  
  533. char *tcp_host (TCPSTREAM *stream)
  534. {
  535.   return stream->host;        /* return host name */
  536. }
  537.  
  538.  
  539. /* TCP/IP return local host for this stream
  540.  * Accepts: TCP/IP stream
  541.  * Returns: local host name for this stream
  542.  */
  543.  
  544. char *tcp_localhost (TCPSTREAM *stream)
  545. {
  546.   return stream->localhost;    /* return local host name */
  547. }
  548.  
  549. /* These functions are only used by rfc822.c for calculating cookies.  So this
  550.  * is good enough.  If anything better is needed fancier functions will be
  551.  * needed.
  552.  */
  553.  
  554.  
  555. /* Return host ID
  556.  */
  557.  
  558. unsigned long gethostid ()
  559. {
  560.   return 0xdeadface;
  561. }
  562.  
  563.  
  564. /* Return random number
  565.  */
  566.  
  567. long random ()
  568. {
  569.   return (long) rand () << 16 + rand ();
  570. }
  571.  
  572.  
  573. /* Return `process ID'
  574.  */
  575.  
  576. long getpid ()
  577. {
  578.   return 1;
  579. }
  580.  
  581.  
  582. /* These two are used for pattern matching in misc.c, but are actually never
  583.  * called in Mac.
  584.  */
  585.  
  586. /* Dummy re_comp -- always return NIL */
  587.  
  588. char *re_comp (char *s)
  589. {
  590.   return NIL;
  591. }
  592.  
  593.  
  594. /* Dummy re_exec -- always return T */
  595.  
  596. long re_exec (char *s)
  597. {
  598.   return T;
  599. }
  600.  
  601. /* Block until event satisfied
  602.  * Called as: while (wait_condition && wait ());
  603.  * Returns T if OK, NIL if user wants to abort
  604.  *
  605.  * Allows user to run a desk accessory, select a different window, or go
  606.  * to another application while waiting for the event to finish.  COMMAND/.
  607.  * will abort the wait.
  608.  * Assumes the Apple menu has the apple character as its first character,
  609.  * and that the main program has disabled all other menus.
  610.  */
  611.  
  612. long wait ()
  613. {
  614.   EventRecord event;
  615.   WindowPtr window;
  616.   MenuInfo **m;
  617.   long r;
  618.   Str255 tmp;
  619.                 /* wait for an event */
  620.   WaitNextEvent (everyEvent,&event,(long) 6,NIL);
  621.   switch (event.what) {        /* got one -- what is it? */
  622.   case mouseDown:        /* mouse clicked */
  623.     switch (FindWindow (event.where,&window)) {
  624.     case inMenuBar:        /* menu bar item? */
  625.                 /* yes, interesting event? */    
  626.       if (r = MenuSelect (event.where)) {
  627.                 /* round-about test for Apple menu */
  628.       if ((*(m = GetMHandle (HiWord (r))))->menuData[1] == appleMark) {
  629.                 /* get desk accessory name */ 
  630.       GetItem (m,LoWord (r),&tmp);
  631.       OpenDeskAcc (tmp);    /* fire it up */
  632.       SetPort (window);    /* put us back at our window */
  633.     }
  634.     else SysBeep (60);    /* the fool forgot to disable it! */
  635.       }
  636.       HiliteMenu (0);        /* unhighlight it */
  637.       break;
  638.     case inContent:        /* some window was selected */
  639.       if (window != FrontWindow ()) SelectWindow (window);
  640.       break;
  641.     default:            /* ignore all others */
  642.       break;
  643.     }
  644.     break;
  645.   case keyDown:            /* key hit - if COMMAND/. then punt */
  646.     if ((event.modifiers & cmdKey) && (event.message & charCodeMask) == '.')
  647.       return NIL;
  648.     break;
  649.   default:            /* ignore all others */
  650.     break;
  651.   }
  652.   return T;            /* try wait test again */
  653. }
  654.